package network;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {

    // serverSocket 只有一个. clientSocket 会给每个客户端都分配一个~
    private ServerSocket serverSocket = null;

    //传入一个端口号
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }


    public void start() throws IOException {
        //频繁地创建销毁线程占资源，利用线程池来解决   cache 缓存   buffer 缓冲区
        ExecutorService executorService = Executors.newCachedThreadPool();

        System.out.println("服务器启动");
        while(true) {
            //需要一个对象来接收
            //创建一个多线程，来接收多个客户端的请求
            Socket clientSocket  = serverSocket.accept();
            //主线程继续往下执行，新创建的线程去执行 processCollection 方法，之后主线程又可以继续进入循环啦
            /*Thread t = new Thread(() -> {
                 try {
                    processCollection(clientSocket);
                } catch (Exception e) {
                    e.printStackTrace();
            }});
            t.start();  //主线程继续往下执行循环*/

            executorService.submit(new Runnable() {

                @Override  //重写
               public void run() {
                    try {
                        processCollection(clientSocket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    // 通过这个方法来处理一个连接.
    // 读取请求
    // 根据请求计算响应
    // 把响应返回给客户端
    private void processCollection(Socket clientSocket) throws IOException {
        //需要用到 InputStream  (从网卡中读取数据)
        // 和  OutputStream (往网卡中写入数据)
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream();) {
            // 没有这个 scanner 和 printWriter, 完全可以!! 但是代价就是得一个字节一个字节扣, 找到哪个是请求的结束标记 \n
            // 不是不能做, 而是代码比较麻烦.
            // 为了简单, 把字节流包装成了更方便的字符流~~
            Scanner scanner = new Scanner(inputStream);
            //都是将字节流转换成字符流，便于后续的操作，若是字节流，则需要一个一个字节的进行判断
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true) {
                // 1. 读取请求
                if(!scanner.hasNext()) {
                    //如果没有下一行，则结束
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //来获取到请求，用字符串接收
                String request = scanner.next();
                // 2. 根据请求计算响应
                String response = process(request);
                // 3. 将响应返回到客户端   不要忘了, 响应里也是要带上换行的.
                printWriter.println(response);
                printWriter.flush();
                //客户端的端口和ip
//                System.out.printf("[%s , %d] req: %s  resp: %d " , clientSocket.getInetAddress().toString(), //toString转换成字符串
//                        clientSocket.getPort(),request,response);
                System.out.printf("[%s , %d] req: %s  resp: %s " , clientSocket.getInetAddress().toString(), //toString转换成字符串
                        clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            clientSocket.close();
        }
    }

    //由于是个回显程序，所以直接返回请求就行了
     public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
